package com.mianle.userapi;

import com.mianle.userapi.config.AccessSafeFilter;
import com.mianle.userapi.config.BaseConfig;
import com.mianle.userapi.config.CorsConfig;
import com.mianle.userapi.config.ResourceConfig;
import com.mianle.userapi.util.SnowflakeSequence;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@EnableTransactionManagement
@SpringBootApplication
@EnableScheduling
@EnableAsync
@MapperScan({"com.mianle.userapi.mapper","com.mianle.core.mapper"})
public class Application {


    @Autowired
    private BaseConfig baseConfig;

    @Autowired
    private CorsConfig corsConfig;

    @Autowired
    private ResourceConfig resourceConfig;


    @Value("${spring.profiles.active}")
    private String profile;



    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }



    @Bean
    public SnowflakeSequence snowflakeSequence() {
        SnowflakeSequence snowflakeSequence = new SnowflakeSequence(0,0);
        return snowflakeSequence;
    }


    @Bean
    public TomcatEmbeddedServletContainerFactory tomcatEmbedded() {
        TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
        tomcat.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
            if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol<?>)) {
                //-1 means unlimited
                ((AbstractHttp11Protocol<?>) connector.getProtocolHandler()).setMaxSwallowSize(-1);
            }
        });
        return tomcat;
    }


    @Bean
    public FilterRegistrationBean AccessSafeFilterRegistration() {

        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new AccessSafeFilter());
        registration.addUrlPatterns("/*");
        registration.addInitParameter("is_debug", baseConfig.getIsDebug()+"");
        registration.addInitParameter("jwt_salt", baseConfig.getJwtSalt());
        registration.addInitParameter("no_check_url", baseConfig.getNoCheckUrl());
        registration.addInitParameter("suffixs", resourceConfig.getNoChecKSuffixs());

        registration.setName("AccessSafeFilter");
        registration.setOrder(2);
        return registration;
    }


    @Bean(name = "corsFilter")
    @ConditionalOnProperty(name = "cors.enable")
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        Map<String, String> urlToAllowOrigins = corsConfig.getUrlToAllowOrigins();

        for (Map.Entry<String, String> entry : urlToAllowOrigins.entrySet()) {

            CorsConfiguration config = new CorsConfiguration();
            config.setAllowCredentials(true);
            String[] allowOrigins = entry.getValue().split(",");

            for (String allowOrigin : allowOrigins) {
                if ("dev".equals(profile)) {
                    config.addAllowedOrigin("*");
                }else{
                    config.addAllowedOrigin(allowOrigin);
                }
            }

            config.addAllowedHeader(CorsConfiguration.ALL);
            config.addAllowedMethod(CorsConfiguration.ALL);
            config.setMaxAge(1000L);
            source.registerCorsConfiguration(entry.getKey(), config);
        }

        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(corsConfig.getFilterOrder());

        return bean;
    }

    @EnableAsync
    @Configuration
    class TaskPoolConfig {

        @Bean("taskExecutor")
        public Executor taskExecutor() {
/*            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            //核心线程数10 - 线程池创建时候初始化的线程数
            executor.setCorePoolSize(10);
            //最大线程数20 - 线程池最大的线程数，只有在缓冲队列满了之后才会申请超过核心线程数的线程
            executor.setMaxPoolSize(20);
            //缓冲队列200 - 用来缓冲执行任务的队列
            executor.setQueueCapacity(200);
            //允许线程的空闲时间60秒 - 当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
            executor.setKeepAliveSeconds(60);
            //线程池名的前缀 - 设置好了之后可以方便我们定位处理任务所在的线程池
            executor.setThreadNamePrefix("taskExecutor-");
            //线程池对拒绝任务的处理策略 - 这里采用了CallerRunsPolicy策略，当线程池没有处理能力的时候，该策略会直接在 execute 方法的调用线程中运行被拒绝的任务；如果执行程序已关闭，则会丢弃该任务
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            return executor;*/

            ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
            executor.setPoolSize(20);
            executor.setThreadNamePrefix("taskExecutor-");
            executor.setWaitForTasksToCompleteOnShutdown(true);
            executor.setAwaitTerminationSeconds(60);
            return executor;

        }
    }

}
